/*
 *
 */
package org.jiscinvolve.astrodabis.barberry.hibernate.data.normal;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.Serializable;

import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ManyToOne;
import javax.persistence.JoinColumn;
import javax.persistence.GenerationType;
import javax.persistence.GeneratedValue;

import org.hibernate.annotations.Index;
import org.hibernate.annotations.NamedQuery;
import org.hibernate.annotations.NamedQueries;
import org.hibernate.annotations.GenericGenerator;

import org.springframework.stereotype.Repository;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;

import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; 

import org.jiscinvolve.astrodabis.barberry.common.core.Job;
import org.jiscinvolve.astrodabis.barberry.common.core.Tag;
import org.jiscinvolve.astrodabis.barberry.common.core.Womble;
import org.jiscinvolve.astrodabis.barberry.common.core.Account;
import org.jiscinvolve.astrodabis.barberry.common.core.Identifier;
import org.jiscinvolve.astrodabis.barberry.common.core.SimpleIdent;
import org.jiscinvolve.astrodabis.barberry.common.core.AbstractIdent;
import org.jiscinvolve.astrodabis.barberry.common.core.exception.*;

import org.jiscinvolve.astrodabis.barberry.common.data.TapRow;
import org.jiscinvolve.astrodabis.barberry.common.data.TapJoin;
import org.jiscinvolve.astrodabis.barberry.common.data.TapTable;
import org.jiscinvolve.astrodabis.barberry.common.data.TapColumn;
import org.jiscinvolve.astrodabis.barberry.common.data.TapService;

import org.jiscinvolve.astrodabis.barberry.common.data.JoinTag;

import org.jiscinvolve.astrodabis.barberry.hibernate.core.AbstractEntity;
import org.jiscinvolve.astrodabis.barberry.hibernate.core.AbstractFactory;

import org.jiscinvolve.astrodabis.barberry.hibernate.annotation.*;

/**
 *
 */
@Entity
@Table(
    name = TapJoinEntity.DB_TABLE_NAME
    )
@org.hibernate.annotations.Table(
    appliesTo = TapJoinEntity.DB_TABLE_NAME,
    indexes = {
        @Index(
            name = TapJoinEntity.DB_INDEX_NAME,
            columnNames = {
                TapJoinEntity.DB_ONE_COL,
                TapJoinEntity.DB_TWO_COL
                }
            )
        }
    )
@NamedQueries(
        {
        @NamedQuery(
            name  = "tapjoin-all",
            query = "FROM TapJoinEntity"
            ),
        @NamedQuery(
            name  = "tapjoin-one",
            query = "FROM TapJoinEntity WHERE one = :one"
            ),
        @NamedQuery(
            name  = "tapjoin-two",
            query = "FROM TapJoinEntity WHERE two = :two"
            ),
        @NamedQuery(
            name  = "tapjoin-one-or-two",
            query = "FROM TapJoinEntity WHERE one = :one OR two = :two"
            ),
        @NamedQuery(
            name  = "tapjoin-one-and-two",
            query = "FROM TapJoinEntity WHERE one = :one AND two = :two"
            ),
        @NamedQuery(
            name  = "tapjoin-one-and-two-or-two-and-one",
            query = "FROM TapJoinEntity WHERE (one = :one AND two = :two) OR (one = :two AND two = :one)"
            )
        }
    )
public class TapJoinEntity
extends AbstractEntity
implements TapJoin
    {

    /**
     * Our debug logger.
     * 
     */
    private static Log log = LogFactory.getLog(TapJoinEntity.class);

    public static final String DB_TABLE_NAME = "normjoins";
    public static final String DB_INDEX_NAME = "normjoins_index";
    public static final String DB_IDENT_COL  = "ident";
    public static final String DB_ONE_COL    = "one";
    public static final String DB_TWO_COL    = "two";

    @Repository
    public static class Factory
    extends AbstractFactory<TapJoin, TapJoinEntity>
    implements TapJoin.Factory
        {

        @Override
        public Identifier ident(String ident)
            {
            if ((ident == null) || ("-".equals(ident)))
                {
                return null ;
                }
            else {
                try {
                    return new SimpleIdent<Long>(
                        Long.valueOf(
                            ident
                            )
                        );
                    }
                catch (NumberFormatException ouch)
                    {
                    throw new IdentFormatException(
                        ouch
                        );
                    }
                }
            }

        @Override
        @CreateObject
        public TapJoin create(TapRow one, TapRow two)
            {
            return createQuick(
                one,
                two
                );
            }

        @Override
        @CreateQuick
        public TapJoin createQuick(TapRow one, TapRow two)
            {
            TapJoin found = select(
                one,
                two
                );
            if (found != null)
                {
                return found ;
                }
            else {
                return hibernate().insert(
                    new TapJoinEntity(
                        one,
                        two
                        )
                    );
                }
            }

        @Override
        @SelectObject
        public TapJoin select(Identifier ident)
            {
            return hibernate().select(
                TapJoinEntity.class,
                ident
                );
            }

        @Override
        @SelectObject
        public Iterable<TapJoin> select()
            {
            return hibernate().iterable(
                hibernate().query(
                    "tapjoin-all"
                    )
                );
            }

        @Override
        @SelectObject
        public Iterable<TapJoin> select(TapRow row, SelectType type)
            {
            if (type == SelectType.SAME)
                {
                return hibernate().iterable(
                    hibernate().query(
                        "tapjoin-one"
                        ).setEntity(
                            "one",
                            row
                            )
                    );
                }
            else if (type == SelectType.SWAP)
                {
                return hibernate().iterable(
                    hibernate().query(
                        "tapjoin-two"
                        ).setEntity(
                            "two",
                            row
                            )
                    );
                }
            else {
                return hibernate().iterable(
                    hibernate().query(
                        "tapjoin-one-or-two"
                        ).setEntity(
                            "one",
                            row
                        ).setEntity(
                            "two",
                            row
                            )
                    );
                }
            }

        @Override
        @SelectObject
        public Iterable<TapJoin> select(TapRow one, TapRow two, SelectType type)
            {
            if (type == SelectType.SAME)
                {
                return hibernate().iterable(
                    hibernate().query(
                        "tapjoin-one-and-two"
                        ).setEntity(
                            "one",
                            one
                        ).setEntity(
                            "two",
                            two
                            )
                    );
                }
            else if (type == SelectType.SWAP)
                {
                return hibernate().iterable(
                    hibernate().query(
                        "tapjoin-one-and-two"
                        ).setEntity(
                            "one",
                            two
                        ).setEntity(
                            "two",
                            one
                            )
                    );
                }
            else {
                return hibernate().iterable(
                    hibernate().query(
                        "tapjoin-one-and-two-or-two-and-one"
                        ).setEntity(
                            "one",
                            one
                        ).setEntity(
                            "two",
                            two
                            )
                    );
                }
            }


        @Override
        @SelectObject
        public TapJoin select(TapRow one, TapRow two)
            {
            return hibernate().first(
                hibernate().query(
                    "tapjoin-one-and-two"
                    ).setEntity(
                        "one",
                        one
                        ).setEntity(
                            "two",
                            two
                            )
                );
            }
        }

    protected TapJoinEntity()
        {
        super();
        }

    protected TapJoinEntity(TapRow one, TapRow two)
        {
        super(true);
        this.one = one ;
        this.two = two ;
        }

    @Id
    @Column(
        name = DB_IDENT_COL
        )
    @GeneratedValue(
        generator="tapdata-hilo"
        )
    @GenericGenerator(
        name="tapdata-hilo",
        strategy="hilo"
        )
    protected Long ident ;

    @Override
    public Identifier ident()
        {
        return new AbstractIdent()
            {
            @Override
            public Serializable value()
                {
                return ident ;
                }
            };
        }

    @ManyToOne(
        fetch = FetchType.LAZY,
        targetEntity = TapRowEntity.class
        )
    @JoinColumn(
        name = DB_ONE_COL,
        unique = false,
        nullable = false,
        updatable = false
        )
    private TapRow one ;

    @Override
    public TapRow one()
        {
        return this.one ;
        }

    @ManyToOne(
        fetch = FetchType.LAZY,
        targetEntity = TapRowEntity.class
        )
    @JoinColumn(
        name = DB_TWO_COL,
        unique = false,
        nullable = false,
        updatable = false
        )
    private TapRow two ;

    @Override
    public TapRow two()
        {
        return this.two ;
        }

    @Override
    public boolean equals(Object that)
        {
        if (this == that)
            {
            return true ;
            }
        if (this.ident == null)
            {
            return false ;
            }
        if (that instanceof TapJoinEntity)
            {
            return this.ident.equals(
                ((TapJoinEntity)that).ident
                );
            }
        if (that instanceof TapJoin)
            {
            return this.ident().equals(
                ((TapJoin)that).ident()
                );
            }
        return false ;
        }

    public JoinTags tags()
        {
        return new JoinTags()
            {

            @Override
            public JoinTag create(Job job, Tag tag)
                {
                return Womble.womble().jointags().create(
                    TapJoinEntity.this,
                    job,
                    tag
                    );
                }

            @Override
            public JoinTag createQuick(Job job, Tag tag)
                {
                return Womble.womble().jointags().createQuick(
                    TapJoinEntity.this,
                    job,
                    tag
                    );
                }

            @Override
            public JoinTag create(Job job, Tag tag, Double value)
                {
                return Womble.womble().jointags().create(
                    TapJoinEntity.this,
                    job,
                    tag,
                    value
                    );
                }

            @Override
            public JoinTag createQuick(Job job, Tag tag, Double value)
                {
                return Womble.womble().jointags().createQuick(
                    TapJoinEntity.this,
                    job,
                    tag,
                    value
                    );
                }

            @Override
            public Iterable<JoinTag> select()
                {
                return Womble.womble().jointags().select(
                    TapJoinEntity.this
                    );
                }

            @Override
            public Iterable<JoinTag> select(Tag tag)
                {
                return Womble.womble().jointags().select(
                    TapJoinEntity.this,
                    tag
                    );
                }

            @Override
            public JoinTag select(Identifier ident)
                {
                return Womble.womble().jointags().select(
                    ident
                    );
                }
            };
        }

    @Override
    public String toString()
        {
        StringBuilder builder = new StringBuilder();
        builder.append(
            "Join["
            );
        builder.append(
            this.ident
            );
        builder.append(
            "]{ "
            );
        builder.append(
            this.one().toString()
            );
        builder.append(
            ", "
            );
        builder.append(
            this.two().toString()
            );
        builder.append(
            "}"
            );
        return builder.toString();
        }
    }

